package num

import (
	"math/big"
)

// NumberType represents the type of number
type NumberType int

const (
	INTEGER_TYPE NumberType = iota
	BIGINTEGER_TYPE
	RATIONAL_TYPE
	REAL_TYPE
	COMPLEX_TYPE
)

type Integer int64
type BigInteger big.Int
type Rational big.Rat
type Real float64
type Complex complex128

// Number is a generic number interface
type Number interface {
	Type() NumberType
	String() string
	ADD(Number) Number
	SUB(Number) Number
	MUL(Number) Number
	DIV(Number) Number
	NEG() Number
}

// Helper functions for type conversion
func ToBigInt(n Number) *big.Int {
	switch v := n.(type) {
	case Integer:
		return big.NewInt(int64(v))
	case BigInteger:
		return (*big.Int)(&v)
	case *BigInteger:
		return (*big.Int)(v)
	default:
		panic("Cannot convert to big.Int")
	}
}

func ToBigRat(n Number) *big.Rat {
	switch v := n.(type) {
	case Integer:
		return big.NewRat(int64(v), 1)
	case BigInteger:
		return new(big.Rat).SetInt((*big.Int)(&v))
	case *BigInteger:
		return new(big.Rat).SetInt((*big.Int)(v))
	case Rational:
		return (*big.Rat)(&v)
	case *Rational:
		return (*big.Rat)(v)
	case Real:
		return new(big.Rat).SetFloat64(float64(v))
	case Complex:
		// For complex numbers, we only convert the real part
		// This is consistent with Go's type conversion rules
		realPart := real(complex128(v))
		return new(big.Rat).SetFloat64(realPart)
	default:
		panic("Cannot convert to big.Rat")
	}
}

func maxType(t1, t2 NumberType) NumberType {
	if t1 > t2 {
		return t1
	}
	return t2
}

// Integer implementation
func (x Integer) Type() NumberType {
	return INTEGER_TYPE
}

func (x Integer) String() string {
	return big.NewInt(int64(x)).String()
}

func (x Integer) ADD(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		// Check for overflow
		if (int64(x) > 0 && int64(yVal) > 0 && int64(x) > (1<<63-1-int64(yVal))) ||
			(int64(x) < 0 && int64(yVal) < 0 && int64(x) < (-1<<63-int64(yVal))) {
			// Overflow occurred, convert to BigInteger
			bigResult := new(big.Int).Add(big.NewInt(int64(x)), big.NewInt(int64(yVal)))
			return (*BigInteger)(bigResult)
		}
		return Integer(int64(x) + int64(yVal))
	case BIGINTEGER_TYPE:
		bigResult := new(big.Int).Add(big.NewInt(int64(x)), ToBigInt(y))
		return (*BigInteger)(bigResult)
	case RATIONAL_TYPE:
		ratResult := new(big.Rat).Add(ToBigRat(x), ToBigRat(y))
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if ratResult.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := ratResult.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(ratResult)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) + float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) + complex128(yVal))
	default:
		return y
	}
}

func (x Integer) SUB(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		result := int64(x) - int64(yVal)
		// Check for overflow
		if (result >= 0) == (int64(x) >= int64(yVal)) {
			return Integer(result)
		}
		// Overflow occurred, convert to BigInteger
		bigResult := new(big.Int).Sub(big.NewInt(int64(x)), big.NewInt(int64(yVal)))
		return (*BigInteger)(bigResult)
	case BIGINTEGER_TYPE:
		bigResult := new(big.Int).Sub(big.NewInt(int64(x)), ToBigInt(y))
		return (*BigInteger)(bigResult)
	case RATIONAL_TYPE:
		ratResult := new(big.Rat).Sub(ToBigRat(x), ToBigRat(y))
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if ratResult.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := ratResult.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(ratResult)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) - float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) - complex128(yVal))
	default:
		return y
	}
}

func (x Integer) MUL(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		result := int64(x) * int64(yVal)
		// Check for overflow
		if int64(x) == 0 || int64(yVal) == 0 {
			return Integer(0)
		}
		if result/int64(x) == int64(yVal) {
			return Integer(result)
		}
		// Overflow occurred
		bigResult := new(big.Int).Mul(big.NewInt(int64(x)), big.NewInt(int64(yVal)))
		return (*BigInteger)(bigResult)
	case BIGINTEGER_TYPE:
		bigX := big.NewInt(int64(x))
		bigY := ToBigInt(y)
		result := new(big.Int).Mul(bigX, bigY)
		return (*BigInteger)(result)
	case RATIONAL_TYPE:
		ratX := big.NewRat(int64(x), 1)
		ratY := ToBigRat(y)
		result := new(big.Rat).Mul(ratX, ratY)
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) * float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) * complex128(yVal))
	default:
		return y
	}
}

func (x Integer) DIV(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		if int64(x)%int64(yVal) == 0 {
			return Integer(int64(x) / int64(yVal))
		}
		ratResult := new(big.Rat).SetFrac(big.NewInt(int64(x)), big.NewInt(int64(yVal)))
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if ratResult.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := ratResult.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(ratResult)
	case BIGINTEGER_TYPE:
		bigX := big.NewInt(int64(x))
		bigY := ToBigInt(y)
		// Check for exact division
		if new(big.Int).Rem(bigX, bigY).Sign() == 0 {
			result := new(big.Int).Div(bigX, bigY)
			// Check if result fits in int64 range
			if result.IsInt64() {
				return Integer(result.Int64())
			}
			return (*BigInteger)(result)
		}
		// Not exact division, convert to rational
		ratResult := new(big.Rat).SetFrac(bigX, bigY)
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if ratResult.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := ratResult.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(ratResult)
	case RATIONAL_TYPE:
		ratX := big.NewRat(int64(x), 1)
		ratY := ToBigRat(y)
		result := new(big.Rat).Quo(ratX, ratY)
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) / float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) / complex128(yVal))
	default:
		return y
	}
}

func (x Integer) NEG() Number {
	// Check for overflow when negating min int64 value
	if int64(x) == -1<<63 {
		bigResult := new(big.Int).Neg(big.NewInt(int64(x)))
		return (*BigInteger)(bigResult)
	}
	return Integer(-int64(x))
}

// BigInteger implementation
func (x BigInteger) Type() NumberType {
	return BIGINTEGER_TYPE
}

func (x BigInteger) String() string {
	return (*big.Int)(&x).String()
}

func (x BigInteger) ADD(y Number) Number {
	bigX := (*big.Int)(&x)
	var bigY *big.Int

	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		bigY = big.NewInt(int64(yVal))
	case BIGINTEGER_TYPE:
		bigY = ToBigInt(y)
	case RATIONAL_TYPE:
		ratX := new(big.Rat).SetInt(bigX)
		ratY := ToBigRat(y)
		result := new(big.Rat).Add(ratX, ratY)
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Real(realPart + float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) + complex128(yVal))
	}

	result := new(big.Int).Add(bigX, bigY)

	// Check if result fits in int64 range
	if result.IsInt64() {
		val := result.Int64()
		return Integer(val)
	}

	return (*BigInteger)(result)
}

func (x BigInteger) SUB(y Number) Number {
	bigX := (*big.Int)(&x)
	var bigY *big.Int

	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		bigY = big.NewInt(int64(yVal))
	case BIGINTEGER_TYPE:
		bigY = ToBigInt(y)
	case RATIONAL_TYPE:
		ratX := new(big.Rat).SetInt(bigX)
		ratY := ToBigRat(y)
		result := new(big.Rat).Sub(ratX, ratY)
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Real(realPart - float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) - complex128(yVal))
	}

	result := new(big.Int).Sub(bigX, bigY)

	// Check if result fits in int64 range
	if result.IsInt64() {
		val := result.Int64()
		return Integer(val)
	}

	return (*BigInteger)(result)
}

func (x BigInteger) MUL(y Number) Number {
	bigX := (*big.Int)(&x)
	var bigY *big.Int

	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		bigY = big.NewInt(int64(yVal))
	case BIGINTEGER_TYPE:
		bigY = ToBigInt(y)
	case RATIONAL_TYPE:
		ratX := new(big.Rat).SetInt(bigX)
		ratY := ToBigRat(y)
		result := new(big.Rat).Mul(ratX, ratY)
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Real(realPart * float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) * complex128(yVal))
	}

	result := new(big.Int).Mul(bigX, bigY)

	// Check if result fits in int64 range
	if result.IsInt64() {
		val := result.Int64()
		return Integer(val)
	}

	return (*BigInteger)(result)
}

func (x BigInteger) DIV(y Number) Number {
	bigX := (*big.Int)(&x)
	var bigY *big.Int

	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		bigY = big.NewInt(int64(yVal))
	case BIGINTEGER_TYPE:
		bigY = ToBigInt(y)
	case RATIONAL_TYPE:
		ratX := new(big.Rat).SetInt(bigX)
		ratY := ToBigRat(y)
		result := new(big.Rat).Quo(ratX, ratY)
		return (*Rational)(result)
	case REAL_TYPE:
		yVal := y.(Real)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Real(realPart / float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		f := new(big.Float).SetInt(bigX)
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) / complex128(yVal))
	}

	// Check for division with remainder
	result := new(big.Int).Div(bigX, bigY)
	remainder := new(big.Int).Rem(bigX, bigY)

	if remainder.Sign() == 0 { // No remainder
		// Check if result fits in int64 range
		if result.IsInt64() {
			val := result.Int64()
			return Integer(val)
		}
		return (*BigInteger)(result)
	} else { // Has remainder, convert to rational
		ratResult := new(big.Rat).SetFrac(bigX, bigY)
		return (*Rational)(ratResult)
	}
}

func (x BigInteger) NEG() Number {
	bigX := (*big.Int)(&x)
	result := new(big.Int).Neg(bigX)

	// Check if result fits in int64 range
	if result.IsInt64() {
		val := result.Int64()
		return Integer(val)
	}

	return (*BigInteger)(result)
}

// Rational implementation
func (x Rational) Type() NumberType {
	return RATIONAL_TYPE
}

func (x Rational) String() string {
	return (*big.Rat)(&x).String()
}

func (x Rational) ADD(y Number) Number {
	switch y.Type() {
	case REAL_TYPE:
		// If y is a real, then the result should be a real
		yVal := y.(Real)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Real(realPart + float64(yVal))
	case COMPLEX_TYPE:
		// If y is a complex, then the result should be a complex
		yVal := y.(Complex)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) + complex128(yVal))
	default:
		// For other types, perform rational addition
		ratX := (*big.Rat)(&x)
		ratY := ToBigRat(y)
		result := new(big.Rat).Add(ratX, ratY)
		
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	}
}

func (x Rational) SUB(y Number) Number {
	switch y.Type() {
	case REAL_TYPE:
		// If y is a real, then the result should be a real
		yVal := y.(Real)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Real(realPart - float64(yVal))
	case COMPLEX_TYPE:
		// If y is a complex, then the result should be a complex
		yVal := y.(Complex)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) - complex128(yVal))
	default:
		// For other types, perform rational subtraction
		ratX := (*big.Rat)(&x)
		ratY := ToBigRat(y)
		result := new(big.Rat).Sub(ratX, ratY)
		
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	}
}

func (x Rational) MUL(y Number) Number {
	switch y.Type() {
	case REAL_TYPE:
		// If y is a real, then the result should be a real
		yVal := y.(Real)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Real(realPart * float64(yVal))
	case COMPLEX_TYPE:
		// If y is a complex, then the result should be a complex
		yVal := y.(Complex)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) * complex128(yVal))
	default:
		// For other types, perform rational multiplication
		ratX := (*big.Rat)(&x)
		ratY := ToBigRat(y)
		result := new(big.Rat).Mul(ratX, ratY)
		
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	}
}

func (x Rational) DIV(y Number) Number {
	switch y.Type() {
	case REAL_TYPE:
		// If y is a real, then the result should be a real
		yVal := y.(Real)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Real(realPart / float64(yVal))
	case COMPLEX_TYPE:
		// If y is a complex, then the result should be a complex
		yVal := y.(Complex)
		f := new(big.Float).SetRat((*big.Rat)(&x))
		realPart, _ := f.Float64()
		return Complex(complex(realPart, 0) / complex128(yVal))
	default:
		// For other types, perform rational division
		ratX := (*big.Rat)(&x)
		ratY := ToBigRat(y)
		result := new(big.Rat).Quo(ratX, ratY)
		
		// If the result is a rational, but its denominator is 1, 
		// then it should be converted into an integer or biginteger
		if result.Denom().Cmp(big.NewInt(1)) == 0 {
			numerator := result.Num()
			// Check if numerator fits in int64 range
			if numerator.IsInt64() {
				return Integer(numerator.Int64())
			}
			return (*BigInteger)(numerator)
		}
		return (*Rational)(result)
	}
}

func (x Rational) NEG() Number {
	ratX := (*big.Rat)(&x)
	result := new(big.Rat).Neg(ratX)
	return (*Rational)(result)
}

// Real implementation
func (x Real) Type() NumberType {
	return REAL_TYPE
}

func (x Real) String() string {
	return big.NewFloat(float64(x)).String()
}

func (x Real) ADD(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Real(float64(x) + float64(yVal))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) + yFloat)
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) + yFloat)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) + float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) + complex128(yVal))
	default:
		return y
	}
}

func (x Real) SUB(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Real(float64(x) - float64(yVal))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) - yFloat)
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) - yFloat)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) - float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) - complex128(yVal))
	default:
		return y
	}
}

func (x Real) MUL(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Real(float64(x) * float64(yVal))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) * yFloat)
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) * yFloat)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) * float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) * complex128(yVal))
	default:
		return y
	}
}

func (x Real) DIV(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Real(float64(x) / float64(yVal))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) / yFloat)
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Real(float64(x) / yFloat)
	case REAL_TYPE:
		yVal := y.(Real)
		return Real(float64(x) / float64(yVal))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex(float64(x), 0) / complex128(yVal))
	default:
		return y
	}
}

func (x Real) NEG() Number {
	return Real(-float64(x))
}

// Complex implementation
func (x Complex) Type() NumberType {
	return COMPLEX_TYPE
}

func (x Complex) String() string {
	c := complex128(x)
	realPart := real(c)
	imagPart := imag(c)

	if imagPart >= 0 {
		return big.NewFloat(realPart).String() + "+" + big.NewFloat(imagPart).String() + "i"
	}
	return big.NewFloat(realPart).String() + big.NewFloat(imagPart).String() + "i"
}

func (x Complex) ADD(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Complex(x + Complex(complex(float64(yVal), 0)))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) + complex(yFloat, 0))
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) + complex(yFloat, 0))
	case REAL_TYPE:
		yVal := y.(Real)
		return Complex(complex128(x) + complex(float64(yVal), 0))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex128(x) + complex128(yVal))
	default:
		return y
	}
}

func (x Complex) SUB(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Complex(complex128(x) - complex(float64(yVal), 0))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) - complex(yFloat, 0))
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) - complex(yFloat, 0))
	case REAL_TYPE:
		yVal := y.(Real)
		return Complex(complex128(x) - complex(float64(yVal), 0))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex128(x) - complex128(yVal))
	default:
		return y
	}
}

func (x Complex) MUL(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Complex(complex128(x) * complex(float64(yVal), 0))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) * complex(yFloat, 0))
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) * complex(yFloat, 0))
	case REAL_TYPE:
		yVal := y.(Real)
		return Complex(complex128(x) * complex(float64(yVal), 0))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex128(x) * complex128(yVal))
	default:
		return y
	}
}

func (x Complex) DIV(y Number) Number {
	switch y.Type() {
	case INTEGER_TYPE:
		yVal := y.(Integer)
		return Complex(complex128(x) / complex(float64(yVal), 0))
	case BIGINTEGER_TYPE:
		yVal := ToBigInt(y)
		f := new(big.Float).SetInt(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) / complex(yFloat, 0))
	case RATIONAL_TYPE:
		yVal := ToBigRat(y)
		f := new(big.Float).SetRat(yVal)
		yFloat, _ := f.Float64()
		return Complex(complex128(x) / complex(yFloat, 0))
	case REAL_TYPE:
		yVal := y.(Real)
		return Complex(complex128(x) / complex(float64(yVal), 0))
	case COMPLEX_TYPE:
		yVal := y.(Complex)
		return Complex(complex128(x) / complex128(yVal))
	default:
		return y
	}
}

func (x Complex) NEG() Number {
	return Complex(-complex128(x))
}

func (x Rational) Num() Number {
	num := (*big.Rat)(&x).Num()
	if num.IsInt64() {
		return Integer(num.Int64())
	}
	return (*BigInteger)(num)
}

func (x Rational) Denom() Number {
	denom := (*big.Rat)(&x).Denom()
	if denom.IsInt64() {
		return Integer(denom.Int64())
	}
	return (*BigInteger)(denom)
}

func IsZero(n Number) bool {
	switch n.Type() {
	case INTEGER_TYPE:
		return n.(Integer) == 0
	case BIGINTEGER_TYPE:
		return ToBigInt(n).Cmp(big.NewInt(0)) == 0
	case RATIONAL_TYPE:
		r := ToBigRat(n)
		return r.Num().Cmp(big.NewInt(0)) == 0
	case REAL_TYPE:
		return n.(Real) == 0.0
	case COMPLEX_TYPE:
		c := complex128(n.(Complex))
		return real(c) == 0.0 && imag(c) == 0.0
	default:
		return false
	}
}

func Equal(a, b Number) bool {
	typeA := a.Type()
	typeB := a.Type()

	// Different number types are not equal
	if typeA != typeB {
		return false
	}

	switch typeA {
	case INTEGER_TYPE:
		intA := a.(Integer)
		intB := b.(Integer)
		return intA == intB
	case BIGINTEGER_TYPE:
		intA := big.Int(a.(BigInteger))
		intB := big.Int(b.(BigInteger))
		return (&intA).Cmp(&intB) == 0
	case RATIONAL_TYPE:
		ratA := big.Rat(a.(Rational))
		ratB := big.Rat(b.(Rational))
		return (&ratA).Cmp(&ratB) == 0
	case REAL_TYPE:
		realA := a.(Real)
		realB := b.(Real)
		return realA == realB
	case COMPLEX_TYPE:
		complexA := a.(Complex)
		complexB := b.(Complex)
		return complexA == complexB
	default:
		return false
	}
}

func Number_CMP(a, b Number) int {
	if a == nil || b == nil {
		panic("RNumber_CMP: nil argument")
	}

	// If both are non-real (Integer/BigInteger/Rational), compare as big.Rat for exactness.
	at := a.Type()
	bt := b.Type()
	if at == bt {
		switch at {
		case INTEGER_TYPE:
			intA := a.(Integer)
			intB := b.(Integer)
			if intA < intB {
				return -1
			} else if intA > intB {
				return 1
			} else {
				return 0
			}
		case BIGINTEGER_TYPE:
			bigA := ToBigInt(a)
			bigB := ToBigInt(b)
			return bigA.Cmp(bigB)
		case RATIONAL_TYPE:
			ratA := ToBigRat(a)
			ratB := ToBigRat(b)
			return ratA.Cmp(ratB)
		case REAL_TYPE:
			realA := a.(Real)
			realB := b.(Real)
			if realA < realB {
				return -1
			} else if realA > realB {
				return 1
			} else {
				return 0
			}
		default:
			panic("Number_CMP: unknown number type")
		}
	}

	higherType := maxType(at, bt)
	switch higherType {
	case BIGINTEGER_TYPE:
		bia := ToBigInt(a)
		bib := ToBigInt(b)
		return bia.Cmp(bib)
	case RATIONAL_TYPE, REAL_TYPE:
		ra := ToBigRat(a)
		rb := ToBigRat(b)
		return ra.Cmp(rb)
	default:
		panic("Number_CMP: unknown number type")

	}
}
